/*
 * DOMCharactersProxy.java
 *
 * Created on September 4, 2006, 1:05 PM
 *
 * (C) 2006 R. Alexander Milowski
 */

package org.infoset.xml.dom;

import java.net.URI;
import java.util.Map;
import org.infoset.xml.Attribute;
import org.infoset.xml.Child;
import org.infoset.xml.Element;
import org.infoset.xml.Name;
import org.w3c.dom.Attr;
import org.w3c.dom.DOMException;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.TypeInfo;

/**
 *
 * @author alex
 */
public class DOMElementProxy extends DOMParentProxy implements org.w3c.dom.Element {
   
   static String XMLNS_NAMESPACE = "http://www.w3.org/2000/xmlns/";
   /** Creates a new instance of DOMElementProxy */
   public DOMElementProxy(Element item,DOMDocumentProxy docProxy) {
      super(item,docProxy);
   }
   public short getNodeType() {
      return Node.ELEMENT_NODE;
   }
   
   public String getNodeValue() {
      return getTextContent();
   }
   
   public void setNodeValue(String value) {
      setTextContent(value);
   }
   
   public String getNodeName() {
      return getTagName();
   }
   
   public String getTagName() {
      Element e = (Element)item;
      String prefix = e.getPrefix();
      return prefix.length()==0 ? e.getName().getLocalName() : prefix+":"+e.getName().getLocalName();
   }
   
   public void setTextContent(String value) {
      Element e = (Element)item;
      e.clear();
      e.addCharacters(value);
   }
   
   public String getTextContent() {
      Element e = (Element)item;
      return e.getText();
   }
   
   public boolean hasAttributes() {
      Element e = (Element)item;
      return e.hasAttributes() || e.hasNamespaceDeclarations();
   }
   
   public NamedNodeMap getAttributes() {
      final Element e = (Element)item;
      //System.out.println("getAttributes called on element "+e.getName());
      final Map <Name,Attribute> atts = e.getAttributes();
      final Object [] nameList = atts.keySet().toArray();
      final Object [] prefixList = e.hasNamespaceDeclarations() ?
                                   e.getNamespaceScope().getDataProxy().keySet().toArray() :
                                   new Object[0];
      return new NamedNodeMap() {
         public int getLength() {
            return nameList.length+prefixList.length;
         }
         public Node getNamedItem(String name) {
            //System.out.println("getNamedItem called ("+name+")");
            return documentProxy.getProxy(atts.get(Name.create(name)));
         }
         public Node getNamedItemNS(String namespaceURI,String name) {
            //System.out.println("getNamedItemNS called ("+namespaceURI+","+name+")");
            if (XMLNS_NAMESPACE.equals(namespaceURI)) {
               URI namespace = e.hasNamespaceDeclarations() ? e.getNamespaceScope().getDataProxy().get(name) : null;
               return namespace==null ? null : new NamespaceAttr(DOMElementProxy.this,name,namespace);
            } else {
               return documentProxy.getProxy(atts.get(new Name(URI.create(namespaceURI),name)));
            }
         }
         public Node item(int index) {
            //System.out.println("item called");
            if (index<nameList.length) {
               return documentProxy.getProxy(atts.get((Name)nameList[index]));
            } else {
               int pindex = index - nameList.length;
               if (pindex<prefixList.length) {
                  URI namespace = e.getNamespaceScope().getNamespace((String)prefixList[pindex]);
                  return new NamespaceAttr(DOMElementProxy.this,(String)prefixList[pindex],namespace);
               } else {
                  throw new ArrayIndexOutOfBoundsException("Index "+index+" is out of bounds.");
               }
            }
         }
         public Node removeNamedItem(String name) {
            return documentProxy.getProxy(atts.remove(Name.create(name)));
         }
         public Node removeNamedItemNS(String namespaceURI,String name) {
            if (XMLNS_NAMESPACE.equals(namespaceURI)) {
               throw new DOMException(DOMException.NOT_SUPPORTED_ERR,"Cannot remove namespace attribute via infoset proxy.");
            }
            return documentProxy.getProxy(atts.remove(new Name(URI.create(namespaceURI),name)));
         }
         public Node setNamedItem(Node att) {
            if (att.getNodeType()!=Node.ATTRIBUTE_NODE) {
               throw new DOMException(DOMException.TYPE_MISMATCH_ERR,"Node is not an attribute");
            }
            if (XMLNS_NAMESPACE.equals(att.getNamespaceURI())) {
               throw new DOMException(DOMException.NOT_SUPPORTED_ERR,"Cannot add namespace attribute via infoset proxy.");
            }
            throw new DOMException(DOMException.NOT_SUPPORTED_ERR,"Adding attributes through NamedNodeMap is not suported.");
         }
         public Node setNamedItemNS(Node att) {
            if (att.getNodeType()!=Node.ATTRIBUTE_NODE) {
               throw new DOMException(DOMException.TYPE_MISMATCH_ERR,"Node is not an attribute");
            }
            if (XMLNS_NAMESPACE.equals(att.getNamespaceURI())) {
               throw new DOMException(DOMException.NOT_SUPPORTED_ERR,"Cannot add namespace attribute via infoset proxy.");
            }
            throw new DOMException(DOMException.NOT_SUPPORTED_ERR,"Adding attributes through NamedNodeMap is not suported.");
         }
      };
   }

   public boolean hasAttributeNS(String namespaceURI,String localName) {
      if (XMLNS_NAMESPACE.equals(namespaceURI)) {
         Element e = (Element)item;
         return e.hasNamespaceDeclarations() ? e.getNamespaceScope().getDataProxy().get(localName)!=null : false;
      } else {
         Name nm = new Name(URI.create(namespaceURI),localName);
         return ((Element)item).getAttributes().get(nm)!=null;
      }
   }
   
   public boolean hasAttribute(String localName) {
      Name nm = new Name(localName);
      return ((Element)item).getAttributes().get(nm)!=null;
   }
   
   public Attr setAttributeNodeNS(Attr att) {
      return setAttributeNode(att);
   }
   public Attr setAttributeNode(Attr att) {
      String namespace = att.getNamespaceURI();
      Element e = (Element)item;
      if (XMLNS_NAMESPACE.equals(namespace)) {
         String nsValue = att.getValue();
         URI uri = nsValue==null || nsValue.length()==0 ? Name.NO_NAMESPACE : URI.create(nsValue);
         e.addNamespaceBinding(att.getLocalName(),uri);
         return new NamespaceAttr(this,att.getLocalName(),uri);
      }
      String localName = att.getLocalName();
      Name nm = namespace==null || namespace.length()==0 ? new Name(localName) : new Name(URI.create(namespace),localName);
      Attribute attItem = e.setAttributeValue(nm,att.getTextContent());
      return (Attr)documentProxy.getProxy(attItem);
   }
   public Attr setAttributeNodeNS(String namespace,String localName,String value) {
      Element e = (Element)item;
      if (XMLNS_NAMESPACE.equals(namespace)) {
         URI uri = value==null || value.length()==0 ? Name.NO_NAMESPACE : URI.create(value);
         e.addNamespaceBinding(localName,uri);
         return new NamespaceAttr(this,localName,uri);
      }
      Name nm = namespace==null || namespace.length()==0 ? new Name(localName) : new Name(URI.create(namespace),localName);
      Attribute attItem = e.setAttributeValue(nm,value);
      return (Attr)documentProxy.getProxy(attItem);
   }
   
   public void setAttributeNS(String namespace,String localName,String value) {
      setAttributeNodeNS(namespace,localName,value);
   }
   
   public void setAttribute(String localName,String value) {
      setAttributeNodeNS(null,localName,value);
   }
   
   public Attr getAttributeNodeNS(String namespace,String localName) {
      //System.out.println("getAttributeNodeNS called");
      if (XMLNS_NAMESPACE.equals(namespace)) {
         Element e = (Element)item;
         URI ns = e.hasNamespaceDeclarations() ? e.getNamespaceScope().getDataProxy().get(localName) : null;
         return ns==null ? null : new NamespaceAttr(this,localName,ns);
      } else {
         Element e = (Element)item;
         Name nm = namespace==null || namespace.length()==0 ? new Name(localName) : new Name(URI.create(namespace),localName);
         Attribute attItem = e.getAttributes().get(nm);
         return (Attr)documentProxy.getProxy(attItem);
      }
   }
   
   public Attr getAttributeNode(String localName) {
      //System.out.println("getAttributeNode called");
      Name nm = new Name(localName);
      Element e = (Element)item;
      Attribute attItem = e.getAttributes().get(nm);
      return (Attr)documentProxy.getProxy(attItem);
   }
   
   public String getAttributeNS(String namespace,String localName) {
      if (XMLNS_NAMESPACE.equals(namespace)) {
         Element e = (Element)item;
         URI ns = e.hasNamespaceDeclarations() ? e.getNamespaceScope().getDataProxy().get(localName) : null;
         return ns==null ? null : ns==Name.NO_NAMESPACE ? "" : ns.toString();
      } else {
         Name nm = namespace==null || namespace.length()==0 ? new Name(localName) : new Name(URI.create(namespace),localName);
         Element e = (Element)item;
         return e.getAttributeValue(nm);
      }
   }
   
   public String getAttribute(String localName) {
      Name nm = new Name(localName);
      Element e = (Element)item;
      return e.getAttributeValue(nm);
   }
   
   public void removeAttributeNS(String namespace,String localName) {
      if (XMLNS_NAMESPACE.equals(namespace)) {
         throw new DOMException(DOMException.NOT_SUPPORTED_ERR,"Cannot remove namespace attribute via infoset proxy.");
      }
      Name nm = namespace==null || namespace.length()==0 ? new Name(localName) : new Name(URI.create(namespace),localName);
      Element e = (Element)item;
      Attribute attItem = e.getAttributes().remove(nm);
   }
   public void removeAttribute(String localName) {
      Name nm = new Name(localName);
      Element e = (Element)item;
      Attribute attItem = e.getAttributes().remove(nm);
   }
   public Attr removeAttributeNode(Attr att) {
      String namespace = att.getNamespaceURI();
      if (XMLNS_NAMESPACE.equals(namespace)) {
         throw new DOMException(DOMException.NOT_SUPPORTED_ERR,"Cannot remove namespace attribute via infoset proxy.");
      }
      String localName = att.getLocalName();
      Name nm = namespace==null || namespace.length()==0 ? new Name(localName) : new Name(URI.create(namespace),localName);
      Element e = (Element)item;
      Attribute attItem = e.getAttributes().remove(nm);
      return (Attr)documentProxy.getProxy(attItem);
   }
   
   public Node getNextSibling() {
      return documentProxy.getProxy(((Child)item).getFollowingSibling());
   }
   public Node getPreviousSibling() {
      return documentProxy.getProxy(((Child)item).getPrecedingSibling());
   }
   
   public String getPrefix() {
      return ((Element)item).getPrefix();
   }
   
   public void setPrefix(String value) {
      Element e = (Element)item;
      e.setPrefix(value);
   }
   
   public void setIdAttributeNode(Attr att,boolean isId) {
      throw new DOMException(DOMException.NOT_SUPPORTED_ERR,"Setting id attributes is not supported.");
   }
   
   public void setIdAttributeNS(String namespaceURI,String localName,boolean isId) {
      throw new DOMException(DOMException.NOT_SUPPORTED_ERR,"Setting id attributes is not supported.");
   }
   
   public void setIdAttribute(String localName,boolean isId) {
      throw new DOMException(DOMException.NOT_SUPPORTED_ERR,"Setting id attributes is not supported.");
   }
   
   public TypeInfo getSchemaTypeInfo() {
      return null;
   }
   
   public Node cloneNode(boolean deep) {
      return new DOMElementProxy((Element)item.copyOfItem(deep),documentProxy);
   }
}
